# %% [markdown]
# **1. 数据生成模块**
# 负责人：钟建洲
# 任务：
# 设计车辆数据生成逻辑（到达时间、停留时长）。
# 生成车位信息（距离入口的距离，如50m、100m等）。
# 输出标准化数据格式供其他模块使用（如JSON或列表）。

# %%
# 代码
import time
import seaborn as sns
from datetime import datetime
import numpy as np
import pandas as pd
from matplotlib.ticker import MaxNLocator
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import heapq
import random
import json
from datetime import datetime, timedelta


def generate_car_data(num_cars, num_spots):
    cars = []
    spots = [f"Spot {i+1}" for i in range(num_spots)]
    # 50m, 100m, 150m, etc.
    spot_distances = [50 + 50 * (i % 5) for i in range(num_spots)]

    for i in range(num_cars):
        arrival_time = datetime.now() + timedelta(hours=random.randint(0, 10))
        duration_hours = random.randint(1, 4)
        duration = timedelta(hours=duration_hours)
        departure_time = arrival_time + duration

        car = {
            "car_id": f"car {i+1}",
            "arrival_time": arrival_time.strftime("%H:%M"),
            "departure_time": departure_time.strftime("%H:%M"),
            "duration": duration_hours,
            "assigned_spot": random.choice(spots)
        }
        cars.append(car)

    return cars, spot_distances


def main():
    num_cars = 10  # 可以调整为10到20之间的任意值
    num_spots = 15     # 可以调整为10到20之间的任意值

    cars, spot_distances = generate_car_data(num_cars, num_spots)

    data = {
        "cars": cars,
        "spots": spot_distances
    }

    print(json.dumps(data, indent=4))


if __name__ == "__main__":
    main()

# %% [markdown]
# **2.贪心算法实现**
# 负责人：黎荣、郭启荣
# 任务：
# 实现贪心算法核心逻辑：新车辆优先分配距离入口最近的空闲车位。
# 处理实时分配请求，更新车位状态。
# 与数据模块和输出模块对接。

# %%


class ParkingScheduler:
    def __init__(self, parking_spots):
        """
        初始化停车场调度系统
        :param parking_spots: 车位列表，每个元素为(距离入口距离, 车位编号)
        """
        # 使用最小堆管理空闲车位（按距离排序）
        self.available_spots = []
        for distance, spot_id in parking_spots:
            heapq.heappush(self.available_spots, (distance, spot_id))

        # 记录占用中的车位 {车位编号: (车辆ID, 离开时间)}
        self.occupied_spots = {}

    def assign_spot(self, vehicle_id, arrival_time, duration):
        """
        为车辆分配车位（贪心算法：选择最近的可用车位）
        :param vehicle_id: 车辆ID
        :param arrival_time: 到达时间（格式: 'HH:MM'）
        :param duration: 停留时长（小时）
        :return: (分配的车位编号, 预计离开时间) 或 None（如果没有可用车位）
        """
        if not self.available_spots:
            return None

        # 获取最近的车位
        distance, spot_id = heapq.heappop(self.available_spots)

        # 计算离开时间
        leave_time = self._calculate_leave_time(arrival_time, duration)

        # 记录占用信息
        self.occupied_spots[spot_id] = (vehicle_id, leave_time)

        return spot_id, leave_time

    def release_spot(self, spot_id):
        """释放车位，将其重新加入可用队列"""
        if spot_id in self.occupied_spots:
            distance = self._get_distance_by_spot_id(spot_id)
            heapq.heappush(self.available_spots, (distance, spot_id))
            del self.occupied_spots[spot_id]

    def _calculate_leave_time(self, arrival_time, duration):
        """计算离开时间（简化版）"""
        hours = int(arrival_time.split(':')[0]) + duration
        return f"{hours}:{arrival_time.split(':')[1]}"

    def _get_distance_by_spot_id(self, spot_id):
        """根据车位ID查找距离（简化版）"""
        for dist, sid in self.available_spots:
            if sid == spot_id:
                return dist
        for sid, (_, _) in self.occupied_spots.items():
            if sid == spot_id:
                return 50  # 默认距离，实际应从初始化数据获取
        return 50


# 测试用例
print("=== 贪心算法测试 ===")
parking_spots = [(50, 1), (100, 2), (150, 3), (200, 4)]
scheduler = ParkingScheduler(parking_spots)

# 测试1: 正常分配车位
result1 = scheduler.assign_spot("A1", "09:00", 2)
print(f"测试1 - 车辆A1分配到车位 {result1[0]} (预期: 1)，预计离开时间 {result1[1]} (预期: 11:00)")

# 测试2: 分配第二个车位
result2 = scheduler.assign_spot("B2", "10:30", 1.5)
print(f"测试2 - 车辆B2分配到车位 {result2[0]} (预期: 2)，预计离开时间 {result2[1]} (预期: 12:00)")

# 测试3: 释放车位1
scheduler.release_spot(1)
print(
    f"测试3 - 车位1已释放，当前可用车位: {[spot[1] for spot in scheduler.available_spots]} (预期包含: [1])")

# 测试4: 再次分配应优先使用最近释放的车位1
result4 = scheduler.assign_spot("C3", "13:00", 3)
print(f"测试4 - 车辆C3分配到车位 {result4[0]} (预期: 1)，预计离开时间 {result4[1]} (预期: 16:00)")

print("=== 测试完成 ===")


# %% [markdown]
# **3. 回溯算法实现**
# 负责人：李浦瑞
# 任务：
# 设计回溯算法处理预约冲突（如两车同时段预约同一车位）。
# 优化回溯效率（如剪枝策略）。
# 与贪心算法模块协同，处理冲突场景。
# **4. 调度系统整合**
# 负责人：黎荣、郭启荣
# 任务：
# 整合贪心和回溯算法，设计调度流程（如先贪心，冲突时触发回溯）。
# 处理车辆动态到达和离开的时序逻辑。
# 确保模块间数据传递的准确性。

# %%
# 代码


class BacktrackingScheduler:
    def __init__(self, parking_spots):
        """
        初始化：记录所有车位与距离（用于冲突处理时重新分配）
        :param parking_spots: [(距离, 车位ID)]
        """
        self.all_spots = sorted(parking_spots, key=lambda x: x[0])  # 按距离排序
        self.assignments = {}  # {车辆ID: (车位ID, 起始时间, 结束时间)}

    def assign_with_backtracking(self, car_list):
        """
        尝试为所有车辆安排停车，处理冲突（使用回溯）
        :param car_list: 每辆车包含 {car_id, arrival_time, duration}
        :return: 分配成功或失败，及所有分配信息
        """
        return self._backtrack(car_list, 0)

    def _backtrack(self, cars, index):
        if index == len(cars):
            return True  # 所有车辆都安排好了

        car = cars[index]
        car_id = car["car_id"]
        start_time = self._str_to_minutes(car["arrival_time"])
        end_time = start_time + car["duration"] * 60

        for distance, spot_id in self.all_spots:
            if self._is_spot_available(spot_id, start_time, end_time):
                self.assignments[car_id] = (spot_id, start_time, end_time)
                if self._backtrack(cars, index + 1):  # 递归处理下一个车辆
                    return True
                del self.assignments[car_id]  # 回溯：取消当前分配

        return False  # 无法为当前车辆找到合适车位

    def _is_spot_available(self, spot_id, start, end):
        """
        判断该车位在[start, end]时间内是否冲突
        """
        for assigned in self.assignments.values():
            s_id, s_start, s_end = assigned
            if s_id == spot_id:
                # 冲突条件：时间重叠
                if not (end <= s_start or start >= s_end):
                    return False
        return True

    def _str_to_minutes(self, time_str):
        """'HH:MM' -> 分钟数"""
        h, m = map(int, time_str.split(":"))
        return h * 60 + m

    def get_assignments(self):
        """返回最终分配结果（修正版）"""
        readable = {}
        for car_id, (spot_id, start, end) in self.assignments.items():
            readable[car_id] = {
                "spot_id": spot_id,
                "car_id": car_id,  # 确保包含car_id
                "arrival_time": f"{start // 60:02d}:{start % 60:02d}",
                "leave_time": f"{end // 60:02d}:{end % 60:02d}"
            }
        return readable



# 模拟数据：调用数据模块的输出
car_list = [
    {"car_id": "A1", "arrival_time": "09:00", "duration": 2},
    {"car_id": "B2", "arrival_time": "10:00", "duration": 2},
    {"car_id": "C3", "arrival_time": "09:30", "duration": 1},
]

parking_spots = [(50, 1), (100, 2), (150, 3)]  # 距离 + 车位ID

# 创建回溯调度器
bt_scheduler = BacktrackingScheduler(parking_spots)

# 执行分配
success = bt_scheduler.assign_with_backtracking(car_list)

if success:
    print("所有车辆成功分配：")
    for car_id, info in bt_scheduler.get_assignments().items():
        print(
            f"车辆{car_id} → 车位{info['spot_id']}，{info['arrival_time']} 至 {info['leave_time']}")
else:
    print("无法为所有车辆安排无冲突车位。")

# %% [markdown]
# **4. 调度系统整合**
# 负责人：黎荣、郭启荣
# 任务：
# 整合贪心和回溯算法，设计调度流程（如先贪心，冲突时触发回溯）。
# 处理车辆动态到达和离开的时序逻辑。
# 确保模块间数据传递的准确性。

# %%


class IntegratedScheduler:
    def __init__(self, parking_spots):
        """
        初始化整合调度系统
        :param parking_spots: [(距离, 车位ID)]
        """
        # 贪心算法组件
        self.greedy_scheduler = ParkingScheduler(parking_spots)

        # 回溯算法组件
        self.backtracking_scheduler = BacktrackingScheduler(parking_spots)

        # 记录所有车辆信息
        self.all_cars = []

        # 冲突记录
        self.conflicts = []

    def process_car(self, car_data):
        """修正后的车辆处理方法"""
        # 先释放已离开的车位
        current_time = self._time_to_minutes(car_data["arrival_time"])
        for spot_id, (_, leave_time) in list(self.greedy_scheduler.occupied_spots.items()):
            leave_min = self._time_to_minutes(leave_time)
            if leave_min <= current_time:
                self.greedy_scheduler.release_spot(spot_id)

        # 尝试贪心分配
        result = self.greedy_scheduler.assign_spot(
            car_data["car_id"],
            car_data["arrival_time"],
            car_data["duration"]
        )

        if result is None:
            self.conflicts.append(car_data["car_id"])
            return False

        spot_id, leave_time = result
        
        # 检查是否与已有分配冲突
        conflict = False
        for car in self.all_cars:
            if car["spot_id"] == spot_id:
                if self._check_time_conflict(
                    car_data["arrival_time"],
                    leave_time,
                    car["arrival_time"],
                    car["leave_time"]
                ):
                    conflict = True
                    break

        if conflict:
            print(f"\n检测到冲突 - 车辆 {car_data['car_id']} 与车位 {spot_id} 现有分配冲突")
            print("触发回溯算法...")
            
            # 添加当前车辆到待分配列表
            temp_car = {
                "car_id": car_data["car_id"],
                "arrival_time": car_data["arrival_time"],
                "duration": car_data["duration"],
                "spot_id": None,
                "leave_time": None
            }
            self.all_cars.append(temp_car)
            
            # 创建用于回溯的车辆列表
            backtrack_cars = [{
                "car_id": car["car_id"],
                "arrival_time": car["arrival_time"],
                "duration": car["duration"]
            } for car in self.all_cars]
            
            success = self.backtracking_scheduler.assign_with_backtracking(backtrack_cars)
            print(f"回溯结果: {'成功' if success else '失败'}")
            
            if success:
                print("同步调度器状态...")
                # 更新分配信息
                self.all_cars = []
                for car_id, (spot_id, start, end) in self.backtracking_scheduler.assignments.items():
                    self.all_cars.append({
                        "car_id": car_id,
                        "arrival_time": f"{start//60:02d}:{start%60:02d}",
                        "leave_time": f"{end//60:02d}:{end%60:02d}",
                        "duration": (end-start)/60,
                        "spot_id": spot_id
                    })
                self._sync_schedulers()
                return True
            else:
                print("回溯失败，记录冲突")
                self.conflicts.append(car_data["car_id"])
                # 移除临时添加的车辆
                self.all_cars = [car for car in self.all_cars if car["car_id"] != car_data["car_id"]]
                # 释放可能已分配的车位
                if spot_id in self.greedy_scheduler.occupied_spots:
                    self.greedy_scheduler.release_spot(spot_id)
                return False
        else:
            # 无冲突，记录分配
            self.all_cars.append({
                "car_id": car_data["car_id"],
                "arrival_time": car_data["arrival_time"],
                "duration": car_data["duration"],
                "spot_id": spot_id,
                "leave_time": leave_time
            })
            return True
    def _check_time_conflict(self, start1, end1, start2, end2):
        """检查两个时间段是否重叠"""
        start1 = self._time_to_minutes(start1)
        end1 = self._time_to_minutes(end1)
        start2 = self._time_to_minutes(start2)
        end2 = self._time_to_minutes(end2)

    # 允许前车刚离开时后车到达 (end1 == start2 不算冲突)
        if end1 <= start2 or start1 >= end2:
            return False
        return True

    def _time_to_minutes(self, time_str):
        """HH:MM -> 分钟数"""
        h, m = map(int, time_str.split(":"))
        return h * 60 + m

    def _sync_schedulers(self):
        """同步两个调度器的状态"""
        # 清空贪心调度器的分配
        self.greedy_scheduler.occupied_spots = {}
        self.greedy_scheduler.available_spots = []

        # 根据回溯结果重建贪心调度器状态
        for car in self.all_cars:
            if car["spot_id"] is not None:
                self.greedy_scheduler.occupied_spots[car["spot_id"]] = (
                    car["car_id"],
                    car["leave_time"]
                )

        # 重建可用车位，确保按距离排序
        available_spots = []
        for distance, spot_id in self.backtracking_scheduler.all_spots:
            if spot_id not in self.greedy_scheduler.occupied_spots:
                heapq.heappush(available_spots, (distance, spot_id))

        # 更新可用车位列表
        self.greedy_scheduler.available_spots = available_spots

    def get_assignments(self):
        """获取最终分配结果（修正版）"""
        assignments = {}
        
        # 合并贪心和回溯的分配结果
        for car in self.all_cars:
            if car["spot_id"] is not None:
                assignments[car["car_id"]] = {
                    "spot_id": car["spot_id"],
                    "car_id": car["car_id"],  # 确保包含car_id
                    "arrival_time": car["arrival_time"],
                    "leave_time": car["leave_time"]
                }
        
        # 添加回溯算法成功分配的车辆
        if hasattr(self, 'backtracking_scheduler'):
            bt_assignments = self.backtracking_scheduler.get_assignments()
            for car_id, info in bt_assignments.items():
                if car_id not in assignments:  # 避免覆盖
                    assignments[car_id] = info
        
        return assignments

    def get_conflicts(self):
        """获取冲突车辆列表"""
        return self.conflicts


# 示例用法
if __name__ == "__main__":
    # 初始化车位
    parking_spots = [(50, 1), (100, 2), (150, 3)]
    
    # 创建整合调度器
    scheduler = IntegratedScheduler(parking_spots)
    
    # 处理车辆 - 确保时间不重叠
    cars_to_schedule = [
        {"car_id": "A1", "arrival_time": "09:00", "duration": 2},  # 09:00-11:00
        {"car_id": "B2", "arrival_time": "11:00", "duration": 2},  # 11:00-13:00
        {"car_id": "C3", "arrival_time": "13:00", "duration": 1},  # 13:00-14:00
        {"car_id": "D4", "arrival_time": "14:00", "duration": 3},  # 14:00-17:00
        {"car_id": "E5", "arrival_time": "17:00", "duration": 2}   # 17:00-19:00
    ]
    
    for car in cars_to_schedule:
        success = scheduler.process_car(car)
        print(f"处理车辆 {car['car_id']}: {'成功' if success else '失败'}")
    
    # 输出结果
    print("\n最终分配结果:")
    for car_id, info in scheduler.get_assignments().items():
        print(f"车辆{car_id} → 车位{info['spot_id']}，{info['arrival_time']}-{info['leave_time']}")
    
    if scheduler.get_conflicts():
        print("\n以下车辆未能分配:", ", ".join(scheduler.get_conflicts()))
    

# %% [markdown]
# **5. 输出与可视化**
# 负责人：廖伟雄
# 任务：
# 生成车位分配表（如“车辆A1→车位3”）。
# 可选：可视化调度结果（如时间轴甘特图或停车场平面图）。
# 设计用户友好的输出格式（控制台/文件/图形界面）。

# %%
# 代码
# %% [markdown]
# **5. 输出与可视化**
# 负责人：廖伟雄
# 任务：
# 生成车位分配表（如"车辆A1→车位3"）。
# 可视化调度结果（时间轴甘特图、停车场平面图、占用率统计图）。
# 设计用户友好的输出格式（控制台/文件/图形界面）。

# %%

class ParkingVisualizer:
    def __init__(self, scheduler):
        """
        修正版可视化工具初始化
        """
        self._scheduler = scheduler
        
        # 设置中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows系统
        plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
        
        self.color_palette = plt.cm.tab20.colors
        self.color_index = 0
        
    @property
    def scheduler(self):
        """提供对scheduler的安全访问"""
        return self._scheduler
        
    def _get_next_color(self):
        """获取下一个颜色"""
        color = self.color_palette[self.color_index % len(self.color_palette)]
        self.color_index += 1
        return color
    
    def plot_combined_dashboard(self):
        """
        绘制综合仪表盘：甘特图、平面图和统计图
        """
        plt.figure(figsize=(16, 12))
        plt.suptitle('停车场调度系统 - 综合仪表盘', fontsize=16)

        # 甘特图
        plt.subplot(2, 2, 1)
        self._plot_gantt_chart(ax=plt.gca())

        # 平面图
        plt.subplot(2, 2, 2)
        self._plot_parking_layout(ax=plt.gca())

        # 占用率统计
        plt.subplot(2, 2, 3)
        self._plot_utilization(ax=plt.gca())

        # 时间分布
        plt.subplot(2, 2, 4)
        self._plot_time_distribution(ax=plt.gca())

        plt.tight_layout()
        plt.show()
    
    def _plot_gantt_chart(self, ax=None):
        """修正后的甘特图绘制方法"""
        if ax is None:
            fig, ax = plt.subplots(figsize=(12, 6))

        assignments = self._scheduler.get_assignments()  # 改为self._scheduler
        if not assignments:
            print("无分配数据可绘制")
            return

        # 准备数据
        spots = sorted({info['spot_id'] for info in assignments.values()})
        car_assignments = []

        for car_id, info in assignments.items():
            start = self._time_to_minutes(info['arrival_time'])
            end = self._time_to_minutes(info['leave_time'])
            car_assignments.append({
                'car_id': car_id,
                'spot': info['spot_id'],
                'start': start,
                'end': end,
                'duration': end - start
            })

        # 为每个车位创建水平条
        for i, spot in enumerate(spots):
            spot_assignments = [a for a in car_assignments if a['spot'] == spot]
            for assign in spot_assignments:
                color = self._get_next_color()
                ax.barh(
                    y=spot,
                    width=assign['duration'],
                    left=assign['start'],
                    height=0.5,
                    color=color,
                    edgecolor='black',
                    label=assign['car_id']
                )
                # 添加车辆ID标签
                ax.text(
                    assign['start'] + assign['duration']/2,
                    spot,
                    assign['car_id'],
                    ha='center',
                    va='center',
                    color='white',
                    fontweight='bold'
                )

        # 设置y轴
        ax.set_yticks(spots)
        ax.set_yticklabels([f'车位 {s}' for s in spots])
        ax.invert_yaxis()  # 顶部显示最近的车位

        # 设置x轴为时间
        min_time = min(a['start'] for a in car_assignments)
        max_time = max(a['end'] for a in car_assignments)
        ax.set_xlim(min_time - 30, max_time + 30)

        # 将分钟数转换为时间格式
        def minutes_to_time(x):
            hours = int(x // 60)
            minutes = int(x % 60)
            return f"{hours:02d}:{minutes:02d}"

        # 设置x轴刻度
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))
        ax.set_xticklabels([minutes_to_time(x) for x in ax.get_xticks()])

        # 添加标签和标题
        ax.set_xlabel('时间')
        ax.set_ylabel('车位')
        ax.set_title('车位分配甘特图')

        # 添加图例（只显示独特的车辆）
        handles, labels = ax.get_legend_handles_labels()
        by_label = dict(zip(labels, handles))
        ax.legend(by_label.values(), by_label.keys(), title='车辆ID',
                  bbox_to_anchor=(1.05, 1), loc='upper left')

    def _plot_parking_layout(self, ax=None):
        assignments = self._scheduler.get_assignments()  # 改为self._scheduler
        occupied_spots = {info['spot_id'] for info in assignments.values()}

        if ax is None:
            fig, ax = plt.subplots(figsize=(10, 6))

        # 获取所有车位信息
        if isinstance(self.scheduler, IntegratedScheduler):
            all_spots = [spot[1]
                         for spot in self.scheduler.backtracking_scheduler.all_spots]
            spot_distances = [spot[0]
                              for spot in self.scheduler.backtracking_scheduler.all_spots]
        else:
            all_spots = [spot[1] for spot in self.scheduler.all_spots]
            spot_distances = [spot[0] for spot in self.scheduler.all_spots]

        # 绘制入口
        entrance = patches.Rectangle(
            (0, 0), 1, 1,
            facecolor='green',
            label='入口'
        )
        ax.add_patch(entrance)
        ax.text(0.5, 0.5, '入口', ha='center', va='center', color='white')

        # 绘制车位
        for i, (spot, distance) in enumerate(zip(all_spots, spot_distances)):
            y_pos = (i + 1) * 1.5
            color = 'red' if spot in occupied_spots else 'lightgreen'

            # 车位矩形
            spot_rect = patches.Rectangle(
                (2, y_pos - 0.5), 3, 1,
                facecolor=color,
                edgecolor='black'
            )
            ax.add_patch(spot_rect)

            # 车位标签
            ax.text(
                3.5, y_pos,
                f'车位 {spot}\n({distance}m)',
                ha='center', va='center'
            )

            # 连接入口的线
            ax.plot(
                [1, 2], [0.5, y_pos],
                color='gray',
                linestyle='--'
            )

        # 设置图形范围
        ax.set_xlim(0, 6)
        ax.set_ylim(0, (len(all_spots) + 1) * 1.5)

        # 隐藏坐标轴
        ax.axis('off')

        # 添加标题和图例
        ax.set_title('停车场平面布局')

        # 创建自定义图例
        occupied_patch = patches.Patch(
            facecolor='red',
            edgecolor='black',
            label='已占用'
        )
        free_patch = patches.Patch(
            facecolor='lightgreen',
            edgecolor='black',
            label='空闲'
        )

        ax.legend(
            handles=[entrance, occupied_patch, free_patch],
            loc='upper right'
        )

    def _plot_utilization(self, ax=None):
        total_spots = len(self._scheduler.greedy_scheduler.available_spots) + \
            len(self._scheduler.greedy_scheduler.occupied_spots)  # 改为self._scheduler
        if ax is None:
            fig, ax = plt.subplots(figsize=(8, 6))

        total_spots = len(self.scheduler.greedy_scheduler.available_spots) + \
            len(self.scheduler.greedy_scheduler.occupied_spots)
        occupied = len(self.scheduler.greedy_scheduler.occupied_spots)
        free = total_spots - occupied
        utilization = occupied / total_spots * 100

        # 饼图
        labels = ['已占用', '空闲']
        sizes = [occupied, free]
        colors = ['red', 'lightgreen']
        explode = (0.1, 0)  # 突出显示已占用部分

        ax.pie(sizes, explode=explode, labels=labels, colors=colors,
               autopct='%1.1f%%', shadow=True, startangle=90)
        ax.axis('equal')  # 保持圆形
        ax.set_title(f'车位利用率 ({utilization:.1f}%)')

    def _plot_time_distribution(self, ax=None):
        assignments = self._scheduler.get_assignments()  # 改为self._scheduler
        if ax is None:
            fig, ax = plt.subplots(figsize=(8, 6))

        assignments = self.scheduler.get_assignments()
        arrival_times = [self._time_to_minutes(
            info['arrival_time']) for info in assignments.values()]

        # 转换为小时
        arrival_hours = [t / 60 for t in arrival_times]

        # 绘制直方图
        sns.histplot(arrival_hours, bins=24, kde=True, ax=ax)
        ax.set_xlabel('到达时间 (小时)')
        ax.set_ylabel('车辆数量')
        ax.set_title('车辆到达时间分布')
        ax.set_xticks(range(0, 25, 2))
        ax.grid(True)

    def _time_to_minutes(self, time_str):
        """HH:MM -> 分钟数"""
        h, m = map(int, time_str.split(":"))
        return h * 60 + m
    
    def generate_assignment_report(self, output_file=None):
        """生成详细的车位分配报告"""
        try:
            assignments = self._scheduler.get_assignments()  # 改为self._scheduler
            conflicts = getattr(self._scheduler, 'get_conflicts', lambda: [])()  # 改为self._scheduler
            
            report = []
            report.append("="*50)
            report.append("停车场调度系统 - 分配报告")
            report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
            report.append("="*50)
            report.append("\n[车位分配情况]")
            
            # 按车位分组显示
            spot_assignments = {}
            for car_id, info in assignments.items():
                spot_id = info['spot_id']
                if spot_id not in spot_assignments:
                    spot_assignments[spot_id] = []
                spot_assignments[spot_id].append(info)
            
            # 显示已分配的车位
            for spot_id in sorted(spot_assignments.keys()):
                report.append(f"\n车位 {spot_id}:")
                for assignment in sorted(spot_assignments[spot_id], key=lambda x: x['arrival_time']):
                    report.append(f"  - 车辆 {assignment['car_id']}: {assignment['arrival_time']} 至 {assignment['leave_time']}")
            
            # 显示未分配的车位
            all_spots = set([spot[1] for spot in self.scheduler.backtracking_scheduler.all_spots])
            assigned_spots = set(spot_assignments.keys())
            free_spots = all_spots - assigned_spots
            if free_spots:
                report.append("\n[空闲车位]")
                report.append(", ".join([f"车位 {s}" for s in sorted(free_spots)]))
            
            # 冲突信息
            if conflicts:
                report.append("\n[冲突信息]")
                report.append(f"以下 {len(conflicts)} 辆车未能分配车位:")
                report.append(", ".join(conflicts))
            
            # 统计信息
            report.append("\n[统计摘要]")
            total_spots = len(all_spots)
            occupied = len(assigned_spots)
            utilization = occupied / total_spots * 100
            report.append(f"车位总数: {total_spots}")
            report.append(f"已占用车位: {occupied}")
            report.append(f"空闲车位: {len(free_spots)}")
            report.append(f"车位利用率: {utilization:.1f}%")
            report.append(f"冲突车辆数: {len(conflicts)}")
            
            full_report = "\n".join(report)
            
            # 输出到文件或控制台
            if output_file:
                with open(output_file, 'w', encoding='utf-8') as f:
                    f.write(full_report)
                print(f"报告已保存至 {output_file}")
            else:
                print(full_report)
            
            return full_report
            
        except Exception as e:
            print(f"生成报告时出错: {str(e)}")
            return "无法生成报告"


# 示例用法
if __name__ == "__main__":
    try:
        # 1. 初始化调度系统
        parking_spots = [(50, 1), (100, 2), (150, 3)]
        scheduler = IntegratedScheduler(parking_spots)

        # 2. 处理车辆
        cars_to_schedule = [
            {"car_id": "A1", "arrival_time": "09:00", "duration": 2},
            {"car_id": "B2", "arrival_time": "10:00", "duration": 2},
            {"car_id": "C3", "arrival_time": "09:30", "duration": 1},
            {"car_id": "D4", "arrival_time": "11:00", "duration": 3},
            {"car_id": "E5", "arrival_time": "09:00", "duration": 4}  # 这个会冲突
        ]

        for car in cars_to_schedule:
            success = scheduler.process_car(car)
            print(f"处理车辆 {car['car_id']}: {'成功' if success else '失败'}")

        # 3. 验证调度器状态
        print("\n当前占用车位:", scheduler.greedy_scheduler.occupied_spots)
        print("可用车位:", [spot[1] for spot in scheduler.greedy_scheduler.available_spots])

        # 4. 创建可视化工具(确保scheduler已正确初始化)
        print("\n创建可视化工具...")
        visualizer = ParkingVisualizer(scheduler)  # 确保传入有效的scheduler
        
        # 5. 生成报告
        print("\n=== 生成详细报告 ===")
        report = visualizer.generate_assignment_report()
        
        # 6. 可视化(仅在有必要时)
        if len(scheduler.get_assignments()) > 0:
            print("\n=== 生成综合仪表盘 ===")
            visualizer.plot_combined_dashboard()
        else:
            print("无有效分配数据，跳过可视化")
            
    except Exception as e:
        print(f"发生错误: {str(e)}")
        import traceback
        traceback.print_exc()

# %% [markdown]
# **6. 测试与优化**
# 负责人：崔健杨
# 任务：
# 编写测试用例（正常/边界/冲突场景）。
# 分析算法性能（如时间复杂度和等待时间指标）。
# 协同其他成员优化代码和逻辑。

# %%

# ====================== 测试用例 ======================


def run_all_tests():
    """运行所有测试用例"""
    print("===== 开始测试 =====")
    test_normal_case()
    print("正常场景测试通过 ✓")
    test_edge_cases()
    print("边界场景测试通过 ✓")
    test_conflict_cases()
    print("冲突场景测试通过 ✓")
    print("===== 所有测试通过 =====")

# 正常场景


def test_normal_case():
    parking_spots = [(50, 1), (100, 2), (150, 3)]
    scheduler = IntegratedScheduler(parking_spots)

    # 更严格的时间安排，确保无重叠
    cars = [
        {"car_id": "A1", "arrival_time": "09:00", "duration": 2},  # 09:00-11:00
        {"car_id": "B2", "arrival_time": "11:05",
            "duration": 1},  # 11:05-12:05 (增加缓冲时间)
        {"car_id": "C3", "arrival_time": "12:10", "duration": 3}   # 12:10-15:10
    ]

    print("\n=== 开始测试正常场景 ===")
    for i, car in enumerate(cars, 1):
        print(f"\n[步骤 {i}] 处理车辆 {car['car_id']}:")
        print(f"到达时间: {car['arrival_time']}, 停留时长: {car['duration']}小时")

        success = scheduler.process_car(car)
        print(f"分配结果: {'成功' if success else '失败'}")

        if success:
            assignments = scheduler.get_assignments()
            current = assignments[car["car_id"]]
            print(f"分配车位: {current['spot_id']}")
            print(f"离开时间: {current['leave_time']}")

        print("当前占用状态:")
        for spot, (vid, ltime) in scheduler.greedy_scheduler.occupied_spots.items():
            print(f"车位 {spot}: 车辆 {vid} 离开时间 {ltime}")

    assignments = scheduler.get_assignments()
    print("\n=== 最终分配结果 ===")
    for car_id, info in assignments.items():
        print(
            f"车辆 {car_id} → 车位 {info['spot_id']} ({info['arrival_time']}-{info['leave_time']})")

    assert len(assignments) == 3
    assert assignments["A1"]["spot_id"] == 1, f"A1应分配到车位1，实际分配到{assignments['A1']['spot_id']}"
    assert assignments["B2"]["spot_id"] == 1, f"B2应分配到车位1，实际分配到{assignments['B2']['spot_id']}"
    assert assignments["C3"]["spot_id"] == 1, f"C3应分配到车位1，实际分配到{assignments['C3']['spot_id']}"
    assert scheduler.get_conflicts() == []
# 边界场景


def test_edge_cases():
    parking_spots = [(50, 1), (100, 2)]
    scheduler = IntegratedScheduler(parking_spots)

    # 边界1: 车辆刚好在离开时间到达
    assert scheduler.process_car(
        {"car_id": "A1", "arrival_time": "09:00", "duration": 2}) == True
    assert scheduler.process_car(
        {"car_id": "B2", "arrival_time": "11:00", "duration": 1}) == True

    # 边界2: 多辆车同时到达
    print("\n=== 测试同时到达场景 ===")
    # 先释放所有车位
    scheduler.greedy_scheduler.available_spots = []
    for distance, spot_id in parking_spots:
        heapq.heappush(scheduler.greedy_scheduler.available_spots,
                       (distance, spot_id))
    scheduler.greedy_scheduler.occupied_spots = {}

    # 同时分配两辆车
    assert scheduler.process_car(
        {"car_id": "C3", "arrival_time": "12:00", "duration": 1}) == True
    assert scheduler.process_car(
        {"car_id": "D4", "arrival_time": "12:00", "duration": 1}) == True

    assignments = scheduler.get_assignments()
    print("\n分配结果:")
    for car_id, info in assignments.items():
        print(f"车辆{car_id} → 车位{info['spot_id']}")

    # 检查两辆车分配到了不同车位
    assigned_spots = [info["spot_id"] for info in assignments.values()]
    assert len(set(assigned_spots)) == 2, "同时到达的车辆应分配到不同车位"
    assert 1 in assigned_spots and 2 in assigned_spots, "应使用所有可用车位"
    assert scheduler.get_conflicts() == []

# 冲突场景


def test_conflict_cases():
    parking_spots = [(50, 1), (100, 2)]
    scheduler = IntegratedScheduler(parking_spots)

    print("\n=== 测试冲突场景 ===")

    # 测试1: 基本冲突
    print("\n测试1: 两辆车同时到达")
    assert scheduler.process_car(
        {"car_id": "A1", "arrival_time": "09:00", "duration": 4}) == True
    assert scheduler.process_car(
        {"car_id": "B2", "arrival_time": "09:00", "duration": 4}) == True

    # 检查分配
    assignments = scheduler.get_assignments()
    print("当前分配:")
    for car_id, info in assignments.items():
        print(f"车辆{car_id} → 车位{info['spot_id']}")

    # 应分配到不同车位
    spots = [info["spot_id"] for info in assignments.values()]
    assert len(set(spots)) == 2, "同时到达的车辆应分配到不同车位"

    # 测试2: 时间重叠
    print("\n测试2: 时间重叠")
    scheduler = IntegratedScheduler(parking_spots)  # 重置

    # A1: 10:00-13:00
    assert scheduler.process_car(
        {"car_id": "A1", "arrival_time": "10:00", "duration": 3}) == True

    # D4: 11:00-14:00 (与A1重叠)
    print("尝试分配重叠时间的车辆D4...")
    result = scheduler.process_car(
        {"car_id": "D4", "arrival_time": "11:00", "duration": 3})
    print(f"结果: {'成功' if result else '失败'}")

    # 应成功分配到另一个车位
    assert result == True
    assignments = scheduler.get_assignments()
    assert assignments["D4"]["spot_id"] == 2

    # 测试3: 车位不足
    print("\n测试3: 车位不足")
    scheduler = IntegratedScheduler([(50, 1)])  # 只有1个车位

    assert scheduler.process_car(
        {"car_id": "A1", "arrival_time": "09:00", "duration": 4}) == True
    assert scheduler.process_car(
        {"car_id": "B2", "arrival_time": "10:00", "duration": 4}) == False

    conflicts = scheduler.get_conflicts()
    assert "B2" in conflicts
    parking_spots = [(50, 1), (100, 2)]
    scheduler = IntegratedScheduler(parking_spots)

    # 冲突1: 车位不足
    assert scheduler.process_car(
        {"car_id": "A1", "arrival_time": "09:00", "duration": 4}) == True
    assert scheduler.process_car(
        {"car_id": "B2", "arrival_time": "09:00", "duration": 4}) == True
    assert scheduler.process_car(
        {"car_id": "C3", "arrival_time": "09:00", "duration": 4}) == False  # 冲突

    # 冲突2: 时间重叠
    assert scheduler.process_car(
        {"car_id": "D4", "arrival_time": "10:00", "duration": 3}) == True
    assert scheduler.process_car(
        {"car_id": "E5", "arrival_time": "11:00", "duration": 2}) == True
    assert scheduler.process_car(
        {"car_id": "F6", "arrival_time": "12:00", "duration": 2}) == True

    conflicts = scheduler.get_conflicts()
    assignments = scheduler.get_assignments()

    assert "C3" in conflicts
    assert len(assignments) == 5  # 6辆车中5辆成功分配
    assert assignments["F6"]["spot_id"] == 1  # 重用已释放车位

# ====================== 性能分析 ======================


def analyze_performance():
    """分析算法性能"""
    print("\n===== 性能分析 =====")

    # 测试不同规模数据
    test_scales = [
        (10, 15),   # 10辆车，15个车位
        (50, 60),   # 50辆车，60个车位
        (100, 120)  # 100辆车，120个车位
    ]

    for num_cars, num_spots in test_scales:
        print(f"\n测试规模: {num_cars}辆车, {num_spots}个车位")

        # 生成测试数据
        parking_spots = [(50*(i+1), i+1) for i in range(num_spots)]
        cars = []
        for i in range(num_cars):
            arrival = f"{random.randint(8, 18):02d}:{random.randint(0, 59):02d}"
            duration = random.randint(1, 4)
            cars.append({
                "car_id": f"car_{i}",
                "arrival_time": arrival,
                "duration": duration
            })

        # 贪心算法性能
        start = time.time()
        greedy_scheduler = ParkingScheduler(parking_spots.copy())
        greedy_conflicts = 0
        for car in cars:
            if not greedy_scheduler.assign_spot(car["car_id"], car["arrival_time"], car["duration"]):
                greedy_conflicts += 1
        greedy_time = time.time() - start

        # 整合算法性能
        start = time.time()
        integrated_scheduler = IntegratedScheduler(parking_spots.copy())
        integrated_conflicts = 0
        for car in cars:
            if not integrated_scheduler.process_car(car):
                integrated_conflicts += 1
        integrated_time = time.time() - start

        # 输出结果
        print(f"贪心算法: 耗时{greedy_time:.4f}s, 冲突数{greedy_conflicts}")
        print(f"整合算法: 耗时{integrated_time:.4f}s, 冲突数{integrated_conflicts}")


# ====================== 主程序 ======================
# 测试修复后的系统
if __name__ == "__main__":
    try:
        # 1. 初始化调度系统
        parking_spots = [(50, 1), (100, 2), (150, 3)]
        scheduler = IntegratedScheduler(parking_spots)

        # 2. 处理车辆
        cars_to_schedule = [
            {"car_id": "A1", "arrival_time": "09:00", "duration": 2},
            {"car_id": "B2", "arrival_time": "10:00", "duration": 2},
            {"car_id": "C3", "arrival_time": "09:30", "duration": 1},
            {"car_id": "D4", "arrival_time": "11:00", "duration": 3},
            {"car_id": "E5", "arrival_time": "09:00", "duration": 4}  # 这个会冲突
        ]

        for car in cars_to_schedule:
            success = scheduler.process_car(car)
            print(f"处理车辆 {car['car_id']}: {'成功' if success else '失败'}")

        # 3. 验证调度器状态
        print("\n当前占用车位:", scheduler.greedy_scheduler.occupied_spots)
        print("可用车位:", [spot[1] for spot in scheduler.greedy_scheduler.available_spots])

        # 4. 创建可视化工具
        print("\n创建可视化工具...")
        visualizer = ParkingVisualizer(scheduler)
        
        # 5. 生成报告
        print("\n=== 生成详细报告 ===")
        report = visualizer.generate_assignment_report()
        
        # 6. 可视化
        if len(scheduler.get_assignments()) > 0:
            print("\n=== 生成综合仪表盘 ===")
            visualizer.plot_combined_dashboard()
        else:
            print("无有效分配数据，跳过可视化")
            
    except Exception as e:
        print(f"发生错误: {str(e)}")
        import traceback
        traceback.print_exc()
